1 using System;
2 using System.Collections.Generic;
3 using UnityEngine;
4
5 namespace UnityStandardAssets.Water
6 {
7 [ExecuteInEditMode]
8 [RequireComponent(typeof(WaterBase))]
9 public class PlanarReflection : MonoBehaviour
10 {
11 public LayerMask reflectionMask;
12 public bool reflectSkybox = false;
13 public Color clearColor = Color.grey;
14 public String reflectionSampler = "_ReflectionTex";
15 public float clipPlaneOffset = 0.07F;
16
17
18 Vector3 m_Oldpos;
19 Camera m_ReflectionCamera;
20 Material m_SharedMaterial;
21 Dictionary<Camera, bool> m_HelperCameras;
22
23
24 public void Start()
25 {
26 m_SharedMaterial = ((WaterBase)gameObject.GetComponent(typeof(WaterBase))).sharedMaterial;
27 }
28
29
30 Camera CreateReflectionCameraFor(Camera cam)
31 {
32 String reflName = gameObject.name + "Reflection" + cam.name;
33 GameObject go = GameObject.Find(reflName);
34
35 if (!go)
36 {
37 go = new GameObject(reflName, typeof(Camera));
38 }
39 if (!go.GetComponent(typeof(Camera)))
40 {
41 go.AddComponent(typeof(Camera));
42 }
43 Camera reflectCamera = go.GetComponent<Camera>();
44
45 reflectCamera.backgroundColor = clearColor;
46 reflectCamera.clearFlags = reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;
47
48 SetStandardCameraParameter(reflectCamera, reflectionMask);
49
50 if (!reflectCamera.targetTexture)
51 {
52 reflectCamera.targetTexture = CreateTextureFor(cam);
53 }
54
55 return reflectCamera;
56 }
57
58
59 void SetStandardCameraParameter(Camera cam, LayerMask mask)
60 {
61 cam.cullingMask = mask & ~(1 << LayerMask.NameToLayer("Water"));
62 cam.backgroundColor = Color.black;
63 cam.enabled = false;
64 }
65
66
67 RenderTexture CreateTextureFor(Camera cam)
68 {
69 RenderTexture rt = new RenderTexture(Mathf.FloorToInt(cam.pixelWidth * 0.5F),
70 Mathf.FloorToInt(cam.pixelHeight * 0.5F), 24);
71 rt.hideFlags = HideFlags.DontSave;
72 return rt;
73 }
74
75
76 public void RenderHelpCameras(Camera currentCam)
77 {
78 if (null == m_HelperCameras)
79 {
80 m_HelperCameras = new Dictionary<Camera, bool>();
81 }
82
83 if (!m_HelperCameras.ContainsKey(currentCam))
84 {
85 m_HelperCameras.Add(currentCam, false);
86 }
87 if (m_HelperCameras[currentCam])
88 {
89 return;
90 }
91
92 if (!m_ReflectionCamera)
93 {
94 m_ReflectionCamera = CreateReflectionCameraFor(currentCam);
95 }
96
97 RenderReflectionFor(currentCam, m_ReflectionCamera);
98
99 m_HelperCameras[currentCam] = true;
100 }
101
102
103 public void LateUpdate()
104 {
105 if (null != m_HelperCameras)
106 {
107 m_HelperCameras.Clear();
108 }
109 }
110
111
112 public void WaterTileBeingRendered(Transform tr, Camera currentCam)
113 {
114 RenderHelpCameras(currentCam);
115
116 if (m_ReflectionCamera && m_SharedMaterial)
117 {
118 m_SharedMaterial.SetTexture(reflectionSampler, m_ReflectionCamera.targetTexture);
119 }
120 }
121
122
123 public void OnEnable()
124 {
125 Shader.EnableKeyword("WATER_REFLECTIVE");
126 Shader.DisableKeyword("WATER_SIMPLE");
127 }
128
129
130 public void OnDisable()
131 {
132 Shader.EnableKeyword("WATER_SIMPLE");
133 Shader.DisableKeyword("WATER_REFLECTIVE");
134 }
135
136
137 void RenderReflectionFor(Camera cam, Camera reflectCamera)
138 {
139 if (!reflectCamera)
140 {
141 return;
142 }
143
144 if (m_SharedMaterial && !m_SharedMaterial.HasProperty(reflectionSampler))
145 {
146 return;
147 }
148
149 reflectCamera.cullingMask = reflectionMask & ~(1 << LayerMask.NameToLayer("Water"));
150
151 SaneCameraSettings(reflectCamera);
152
153 reflectCamera.backgroundColor = clearColor;
154 reflectCamera.clearFlags = reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;
155 if (reflectSkybox)
156 {
157 if (cam.gameObject.GetComponent(typeof(Skybox)))
158 {
159 Skybox sb = (Skybox)reflectCamera.gameObject.GetComponent(typeof(Skybox));
160 if (!sb)
161 {
162 sb = (Skybox)reflectCamera.gameObject.AddComponent(typeof(Skybox));
163 }
164 sb.material = ((Skybox)cam.GetComponent(typeof(Skybox))).material;
165 }
166 }
167
168 GL.invertCulling = true;
169
170 Transform reflectiveSurface = transform; //waterHeight;
171
172 Vector3 eulerA = cam.transform.eulerAngles;
173
174 reflectCamera.transform.eulerAngles = new Vector3(-eulerA.x, eulerA.y, eulerA.z);
175 reflectCamera.transform.position = cam.transform.position;
176
177 Vector3 pos = reflectiveSurface.transform.position;
178 pos.y = reflectiveSurface.position.y;
179 Vector3 normal = reflectiveSurface.transform.up;
180 float d = -Vector3.Dot(normal, pos) - clipPlaneOffset;
181 Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
182
183 Matrix4x4 reflection = Matrix4x4.zero;
184 reflection = CalculateReflectionMatrix(reflection, reflectionPlane);
185 m_Oldpos = cam.transform.position;
186 Vector3 newpos = reflection.MultiplyPoint(m_Oldpos);
187
188 reflectCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
189
190 Vector4 clipPlane = CameraSpacePlane(reflectCamera, pos, normal, 1.0f);
191
192 Matrix4x4 projection = cam.projectionMatrix;
193 projection = CalculateObliqueMatrix(projection, clipPlane);
194 reflectCamera.projectionMatrix = projection;
195
196 reflectCamera.transform.position = newpos;
197 Vector3 euler = cam.transform.eulerAngles;
198 reflectCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);
199
200 reflectCamera.Render();
201
202 GL.invertCulling = false;
203 }
204
205
206 void SaneCameraSettings(Camera helperCam)
207 {
208 helperCam.depthTextureMode = DepthTextureMode.None;
209 helperCam.backgroundColor = Color.black;
210 helperCam.clearFlags = CameraClearFlags.SolidColor;
211 helperCam.renderingPath = RenderingPath.Forward;
212 }
213
214
215 static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane)
216 {
217 Vector4 q = projection.inverse * new Vector4(
218 Sgn(clipPlane.x),
219 Sgn(clipPlane.y),
220 1.0F,
221 1.0F
222 );
223 Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));
224 // third row = clip plane - fourth row
225 projection[2] = c.x - projection[3];
226 projection[6] = c.y - projection[7];
227 projection[10] = c.z - projection[11];
228 projection[14] = c.w - projection[15];
229
230 return projection;
231 }
232
233
234 static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, Vector4 plane)
235 {
236 reflectionMat.m00 = (1.0F - 2.0F * plane[0] * plane[0]);
237 reflectionMat.m01 = (- 2.0F * plane[0] * plane[1]);
238 reflectionMat.m02 = (- 2.0F * plane[0] * plane[2]);
239 reflectionMat.m03 = (- 2.0F * plane[3] * plane[0]);
240
241 reflectionMat.m10 = (- 2.0F * plane[1] * plane[0]);
242 reflectionMat.m11 = (1.0F - 2.0F * plane[1] * plane[1]);
243 reflectionMat.m12 = (- 2.0F * plane[1] * plane[2]);
244 reflectionMat.m13 = (- 2.0F * plane[3] * plane[1]);
245
246 reflectionMat.m20 = (- 2.0F * plane[2] * plane[0]);
247 reflectionMat.m21 = (- 2.0F * plane[2] * plane[1]);
248 reflectionMat.m22 = (1.0F - 2.0F * plane[2] * plane[2]);
249 reflectionMat.m23 = (- 2.0F * plane[3] * plane[2]);
250
251 reflectionMat.m30 = 0.0F;
252 reflectionMat.m31 = 0.0F;
253 reflectionMat.m32 = 0.0F;
254 reflectionMat.m33 = 1.0F;
255
256 return reflectionMat;
257 }
258
259
260 static float Sgn(float a)
261 {
262 if (a > 0.0F)
263 {
264 return 1.0F;
265 }
266 if (a < 0.0F)
267 {
268 return -1.0F;
269 }
270 return 0.0F;
271 }
272
273
274 Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
275 {
276 Vector3 offsetPos = pos + normal * clipPlaneOffset;
277 Matrix4x4 m = cam.worldToCameraMatrix;
278 Vector3 cpos = m.MultiplyPoint(offsetPos);
279 Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;
280
281 return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
282 }
283 }
284 }